/**
 * @file energy_data.h
 * @brief 电能量数据模块 - 处理电能量相关的所有数据类型
 * @details 本头文件定义了104协议中电能量数据类型的结构体和操作函数，
 *          包括累计量、电能量命令、电能量冻结命令等。
 *          支持完整的数据创建、转换和验证功能。
 * 
 * @author zyb
 * @date 2025-10-12
 * @version 1.0
 * 
 * @note 所有数据类型都遵循IEC 60870-5-104协议规范
 * @warning 电能量数据涉及计量，需要严格验证
 * 
 * @see protocol_types.h
 * @see error_codes.h
 * @see validation_utils.h
 */

#ifndef ENERGY_DATA_H
#define ENERGY_DATA_H

#include "../../common/types/include/protocol_types.h"

#ifdef __cplusplus
extern "C" {
#endif

/* ==================== 电能量数据类型定义 ==================== */

/**
 * @brief 电能量命令限定词 (Energy Command Qualifier)
 */
typedef struct {
    uint8_t qcc : 5;    /* 电能量命令限定词 */
    uint8_t res : 3;    /* 预留 */
} qcc_t;

/**
 * @brief 累计量，短浮点数 (M_IT_NB_1)
 */
typedef struct {
    info_addr_t info_address;   /* 信息对象地址 */
    float energy_value;          /* 电能量值 (IEEE 754短浮点数) */
    qds_t quality;              /* 品质描述词 */
} M_IT_NB_1_t;

/**
 * @brief 带CP56Time2a时标的累计量，短浮点数 (M_IT_TC_1)
 */
typedef struct {
    info_addr_t info_address;   /* 信息对象地址 */
    float energy_value;          /* 电能量值 (IEEE 754短浮点数) */
    qds_t quality;              /* 品质描述词 */
    cp56time2a_t timestamp;     /* CP56Time2a时间戳 */
} M_IT_TC_1_t;

/**
 * @brief 电能量命令结构 (C_IC_NA_1)
 */
typedef struct {
    info_addr_t info_address;   /* 信息对象地址 */
    qoc_t qualifier;           /* 命令限定词 */
} C_IC_NA_1_energy_t;

/**
 * @brief 电能量数据类型枚举
 */
typedef enum {
    ENERGY_TYPE_CURRENT_POSITIVE_ACTIVE = 0,      /* 当前正向有功电能 */
    ENERGY_TYPE_CURRENT_POSITIVE_REACTIVE = 1,     /* 当前正向无功电能 */
    ENERGY_TYPE_CURRENT_QUADRANT1_REACTIVE = 2,   /* 当前一象限无功电能 */
    ENERGY_TYPE_CURRENT_QUADRANT4_REACTIVE = 3,    /* 当前四象限无功电能 */
    ENERGY_TYPE_CURRENT_NEGATIVE_ACTIVE = 4,        /* 当前反向有功电能 */
    ENERGY_TYPE_CURRENT_NEGATIVE_REACTIVE = 5,     /* 当前反向无功电能 */
    ENERGY_TYPE_CURRENT_QUADRANT2_REACTIVE = 6,    /* 当前二象限无功电能 */
    ENERGY_TYPE_CURRENT_QUADRANT3_REACTIVE = 7,    /* 当前三象限无功电能 */
    ENERGY_TYPE_FREEZE_15MIN_POSITIVE_ACTIVE = 8,   /* 15分钟冻结正向有功电能 */
    ENERGY_TYPE_FREEZE_15MIN_POSITIVE_REACTIVE = 9, /* 15分钟冻结正向无功电能 */
    ENERGY_TYPE_FREEZE_15MIN_QUADRANT1_REACTIVE = 10, /* 15分钟冻结一象限无功电能 */
    ENERGY_TYPE_FREEZE_15MIN_QUADRANT4_REACTIVE = 11, /* 15分钟冻结四象限无功电能 */
    ENERGY_TYPE_FREEZE_15MIN_NEGATIVE_ACTIVE = 12,  /* 15分钟冻结反向有功电能 */
    ENERGY_TYPE_FREEZE_15MIN_NEGATIVE_REACTIVE = 13, /* 15分钟冻结反向无功电能 */
    ENERGY_TYPE_FREEZE_15MIN_QUADRANT2_REACTIVE = 14, /* 15分钟冻结二象限无功电能 */
    ENERGY_TYPE_FREEZE_15MIN_QUADRANT3_REACTIVE = 15, /* 15分钟冻结三象限无功电能 */
    ENERGY_TYPE_FREEZE_DAY_POSITIVE_ACTIVE = 16,   /* 日冻结正向有功电能 */
    ENERGY_TYPE_FREEZE_DAY_POSITIVE_REACTIVE = 17,  /* 日冻结正向无功电能 */
    ENERGY_TYPE_FREEZE_DAY_QUADRANT1_REACTIVE = 18, /* 日冻结一象限无功电能 */
    ENERGY_TYPE_FREEZE_DAY_QUADRANT4_REACTIVE = 19, /* 日冻结四象限无功电能 */
    ENERGY_TYPE_FREEZE_DAY_NEGATIVE_ACTIVE = 20,    /* 日冻结反向有功电能 */
    ENERGY_TYPE_FREEZE_DAY_NEGATIVE_REACTIVE = 21,  /* 日冻结反向无功电能 */
    ENERGY_TYPE_FREEZE_DAY_QUADRANT2_REACTIVE = 22, /* 日冻结二象限无功电能 */
    ENERGY_TYPE_FREEZE_DAY_QUADRANT3_REACTIVE = 23, /* 日冻结三象限无功电能 */
    ENERGY_TYPE_FLOW_REVERSE_POSITIVE_ACTIVE = 24,  /* 潮流变化冻结正向有功电能 */
    ENERGY_TYPE_FLOW_REVERSE_POSITIVE_REACTIVE = 25, /* 潮流变化冻结正向无功电能 */
    ENERGY_TYPE_FLOW_REVERSE_QUADRANT1_REACTIVE = 26, /* 潮流变化冻结一象限无功电能 */
    ENERGY_TYPE_FLOW_REVERSE_QUADRANT4_REACTIVE = 27, /* 潮流变化冻结四象限无功电能 */
    ENERGY_TYPE_FLOW_REVERSE_NEGATIVE_ACTIVE = 28,   /* 潮流变化冻结反向有功电能 */
    ENERGY_TYPE_FLOW_REVERSE_NEGATIVE_REACTIVE = 29, /* 潮流变化冻结反向无功电能 */
    ENERGY_TYPE_FLOW_REVERSE_QUADRANT2_REACTIVE = 30, /* 潮流变化冻结二象限无功电能 */
    ENERGY_TYPE_FLOW_REVERSE_QUADRANT3_REACTIVE = 31  /* 潮流变化冻结三象限无功电能 */
} energy_type_t;

/**
 * @brief 电能量回路配置
 */
typedef struct {
    u8 circuit_id;              /* 回路编号 (1-8) */
    char name[32];              /* 回路名称 */
    u8 energy_types_count;      /* 电能量类型数量 */
    energy_type_t* energy_types; /* 电能量类型数组 */
} energy_circuit_config_t;

/**
 * @brief 电能量数据点
 */
typedef struct {
    u16 point_id;               /* 点号 */
    energy_type_t energy_type;  /* 电能量类型 */
    u8 circuit_id;              /* 回路编号 */
    float energy_value;          /* 电能量值 */
    cp56time2a_t timestamp;     /* 时间戳 */
    qds_t quality;              /* 品质描述词 */
} energy_data_point_t;

/**
 * @brief 电能量数据组
 */
typedef struct {
    energy_data_point_t* data_points; /* 电能量数据点数组 */
    u32 count;                        /* 数据点个数 */
    u32 max_count;                    /* 最大数据点个数 */
} energy_data_group_t;

/* ==================== 电能量数据转换函数 ==================== */

/**
 * @brief 累计量转换为字节数组
 * @details 将累计量结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化。
 * 
 * @param energy 累计量结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据序列化
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_energy_value
 * @see validate_energy_value_struct
 * @see buffer_write_u32
 * @see buffer_write_f32
 * @see buffer_write_u8
 */
protocol_error_t energy_value_to_bytes(const M_IT_NB_1_t* energy,
                                       u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为累计量
 * @details 将104协议规定的字节数组格式转换为累计量结构。
 *          按照协议规范进行数据反序列化。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param energy 累计量结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据反序列化
 * @warning 数据长度必须足够容纳累计量
 * 
 * @see energy_value_to_bytes
 * @see validate_energy_value_struct
 * @see buffer_read_u32
 * @see buffer_read_f32
 * @see buffer_read_u8
 */
protocol_error_t bytes_to_energy_value(const u8* data, u32 length,
                                       M_IT_NB_1_t* energy);

/**
 * @brief 带时标的累计量转换为字节数组
 * @details 将带时标的累计量结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化，包含时间戳信息。
 * 
 * @param energy 带时标的累计量结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据序列化，包含时间戳
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_energy_value_with_time
 * @see validate_energy_value_with_time
 * @see buffer_write_u32
 * @see buffer_write_f32
 * @see buffer_write_u8
 * @see cp56time2a_to_bytes
 */
protocol_error_t energy_value_with_time_to_bytes(const M_IT_TC_1_t* energy,
                                                 u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为带时标的累计量
 * @details 将104协议规定的字节数组格式转换为带时标的累计量结构。
 *          按照协议规范进行数据反序列化，包含时间戳信息。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param energy 带时标的累计量结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据反序列化，包含时间戳
 * @warning 数据长度必须足够容纳带时标的累计量
 * 
 * @see energy_value_with_time_to_bytes
 * @see validate_energy_value_with_time
 * @see buffer_read_u32
 * @see buffer_read_f32
 * @see buffer_read_u8
 * @see bytes_to_cp56time2a
 */
protocol_error_t bytes_to_energy_value_with_time(const u8* data, u32 length,
                                                 M_IT_TC_1_t* energy);

/**
 * @brief 电能量命令转换为字节数组
 * @details 将电能量命令结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化。
 * 
 * @param command 电能量命令结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据序列化
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_energy_command
 * @see validate_energy_command
 * @see buffer_write_u32
 * @see buffer_write_u8
 */
protocol_error_t energy_command_to_bytes(const C_IC_NA_1_energy_t* command,
                                         u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为电能量命令
 * @details 将104协议规定的字节数组格式转换为电能量命令结构。
 *          按照协议规范进行数据反序列化。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param command 电能量命令结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据反序列化
 * @warning 数据长度必须足够容纳电能量命令
 * 
 * @see energy_command_to_bytes
 * @see validate_energy_command
 * @see buffer_read_u32
 * @see buffer_read_u8
 */
protocol_error_t bytes_to_energy_command(const u8* data, u32 length,
                                         C_IC_NA_1_energy_t* command);

/* ==================== 验证函数 ==================== */

/**
 * @brief 验证累计量
 * @details 验证累计量结构的完整性和有效性。
 *          检查地址、数值和品质描述词的有效性。
 * 
 * @param energy 累计量结构，不能为NULL
 * @return 验证结果
 *         - 1: 累计量有效
 *         - 0: 累计量无效
 * 
 * @note 验证地址、数值和品质描述词的有效性
 * @warning 输入指针不能为NULL
 * 
 * @see is_info_address_valid
 * @see is_normalized_value_valid
 * @see is_qds_valid
 * @see M_IT_NB_1_t
 */
int validate_energy_value_struct(const M_IT_NB_1_t* energy);

/**
 * @brief 验证带时标的累计量
 * @details 验证带时标的累计量结构的完整性和有效性。
 *          检查地址、数值、品质描述词和时间戳的有效性。
 * 
 * @param energy 带时标的累计量结构，不能为NULL
 * @return 验证结果
 *         - 1: 带时标的累计量有效
 *         - 0: 带时标的累计量无效
 * 
 * @note 验证地址、数值、品质描述词和时间戳的有效性
 * @warning 输入指针不能为NULL
 * 
 * @see is_info_address_valid
 * @see is_normalized_value_valid
 * @see is_qds_valid
 * @see is_cp56time2a_valid
 * @see M_IT_TC_1_t
 */
int validate_energy_value_with_time(const M_IT_TC_1_t* energy);

/**
 * @brief 验证电能量命令
 * @details 验证电能量命令结构的完整性和有效性。
 *          检查地址和命令限定词的有效性。
 * 
 * @param command 电能量命令结构，不能为NULL
 * @return 验证结果
 *         - 1: 电能量命令有效
 *         - 0: 电能量命令无效
 * 
 * @note 验证地址和命令限定词的有效性
 * @warning 输入指针不能为NULL
 * 
 * @see is_info_address_valid
 * @see is_qoc_valid
 * @see C_IC_NA_1_energy_t
 */
int validate_energy_command(const C_IC_NA_1_energy_t* command);

/* ==================== 辅助函数 ==================== */

/**
 * @brief 获取累计量大小
 * @details 获取累计量结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 累计量大小（字节）
 * 
 * @note 累计量大小：8字节（地址3字节 + 值4字节 + QDS1字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_energy_value_with_time_size
 * @see get_energy_command_size
 * @see M_IT_NB_1_t
 */
u32 get_energy_value_size(void);

/**
 * @brief 获取带时标的累计量大小
 * @details 获取带时标的累计量结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 带时标的累计量大小（字节）
 * 
 * @note 带时标的累计量大小：15字节（地址3字节 + 值4字节 + QDS1字节 + 时间戳7字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_energy_value_size
 * @see get_energy_command_size
 * @see M_IT_TC_1_t
 */
u32 get_energy_value_with_time_size(void);

/**
 * @brief 获取电能量命令大小
 * @details 获取电能量命令结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 电能量命令大小（字节）
 * 
 * @note 电能量命令大小：4字节（地址3字节 + QCC1字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_energy_value_size
 * @see get_energy_value_with_time_size
 * @see C_IC_NA_1_energy_t
 */
u32 get_energy_command_size(void);

/**
 * @brief 获取电能量类型名称
 * @details 根据电能量类型枚举值获取对应的中文名称。
 *          用于调试和日志输出。
 * 
 * @param type 电能量类型枚举值
 * @return 电能量类型名称字符串
 * 
 * @note 返回的字符串是静态的，不需要释放
 * @warning 无效类型返回"未知类型"
 * 
 * @see energy_type_t
 * @see get_energy_type_description
 */
const char* get_energy_type_name(energy_type_t type);

/**
 * @brief 获取电能量类型描述
 * @details 根据电能量类型枚举值获取对应的详细描述。
 *          用于调试和日志输出。
 * 
 * @param type 电能量类型枚举值
 * @return 电能量类型描述字符串
 * 
 * @note 返回的字符串是静态的，不需要释放
 * @warning 无效类型返回"未知类型"
 * 
 * @see energy_type_t
 * @see get_energy_type_name
 */
const char* get_energy_type_description(energy_type_t type);

/**
 * @brief 验证电能量命令限定词
 * @details 验证QCC（电能量命令限定词）是否在104协议规定的有效范围内。
 *          检查保留位是否都为0。
 * 
 * @param qoc 电能量命令限定词
 * @return 验证结果
 *         - 1: QCC有效
 *         - 0: QCC无效
 * 
 * @note QCC只有5位有效，其他位必须为0
 * @warning 保留位必须为0
 * 
 * @see is_scq_valid
 * @see is_dcq_valid
 * @see qcc_t
 */
int is_qoc_valid_energy(const qoc_t* qoc);

#ifdef __cplusplus
}
#endif

#endif /* ENERGY_DATA_H */
